﻿using System;
using Common;
using Hangfire;
using Hangfire.HttpJob;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using BaseRepository.Redis;
using Hangfire.Client;
using Hangfire.Common;
using Hangfire.Dashboard.BasicAuthorization;
using Hangfire.Redis.StackExchange;
using Hangfire.Server;
using Hangfire.States;
using Hangfire.Storage;
using Serilog;

namespace Extensions.Services
{
    // 定时任务单独一个服务  所有就不统计注入了  在项目中单独注入
    public static class AddHangfire
    {
        public static IServiceCollection AddHangfireSetup(this IServiceCollection service)
        {
            ArgumentNullException.ThrowIfNull(service);
            var hangfireConfig = Appsettings.app<HangfireConfig>("Hangfire");
            if (hangfireConfig == null || !hangfireConfig.Enable)
            {
                return service;
            }

            var redisConfig = Appsettings.app<RedisConfig>("Redis");
            if (redisConfig == null || !redisConfig.Enable)
            {
                throw new Exception("hangfire 必须取用redis  请检查redis的配置");
            }

            service.AddHangfire(configuration =>
            {
                // 使用redis 来存储信息  
                configuration
                    .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
                    .UseRedisStorage(RedisManager.Instance, new RedisStorageOptions()
                    {
                        // reids的库
                        Db = hangfireConfig.DataBase
                        // 这些直接用它默认的参数就好了  不用我们自己设置
                        // 最大的成功存储数  防止数据无限制的增长
                        //SucceededListSize = 10000,
                        // 最大的失败存储数
                        //DeletedListSize = 1000,
                        //ExpiryCheckInterval = TimeSpan.FromHours(1),
                        //InvisibilityTimeout = TimeSpan.FromHours(3),
                        //UseTransactions = true
                    })
                    .UseHangfireHttpJob();
            });
            
            service.AddHangfireServer(options =>
            {
                options.ServerName = "MyServer";
                // 检测时间
                options.SchedulePollingInterval = TimeSpan.FromSeconds(hangfireConfig.SchedulePollingInterval==0?1:hangfireConfig.SchedulePollingInterval);
                options.WorkerCount = hangfireConfig.WorkerCount == 0
                    ? Math.Max(Environment.ProcessorCount, 20)
                    : hangfireConfig.WorkerCount;
                //options.ServerCheckInterval = TimeSpan.FromSeconds(1);
                //心跳时间
                //options.HeartbeatInterval = TimeSpan.FromMilliseconds(1000);
            });

            return service;
        }

        public static IApplicationBuilder UseHangfireSetup(this IApplicationBuilder app)
        {
            ArgumentNullException.ThrowIfNull(app);
            var hangfireConfig = Appsettings.app<HangfireConfig>("Hangfire");
            if (hangfireConfig == null || !hangfireConfig.Enable)
            {
                return app;
            }
            // 使用依赖注入
            GlobalConfiguration.Configuration.UseActivator(new HangfireActivator(app.ApplicationServices));
            // 这是全局注册  如果注册了  所有的方法都会执行
            // 如果使用了依赖注入 那么hangfire的过滤器的标签 只能加在接口上才生效
            //GlobalJobFilters.Filters.Add(new LogEverythingAttribute());
            GlobalStateHandlers.Handlers.Add(new SucceededStateExpireHandler(hangfireConfig.JobExpire));
            var options = new DashboardOptions()
            {
                Authorization = new[]
                {
                    new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions()
                    {
                        RequireSsl = false,
                        SslRedirect = false,
                        LoginCaseSensitive = true,
                        Users = new[]
                        {
                            new BasicAuthAuthorizationUser()
                            {
                                Login =string.IsNullOrWhiteSpace(hangfireConfig.UserLogin)? "admin":hangfireConfig.UserLogin,
                                PasswordClear =string.IsNullOrWhiteSpace(hangfireConfig.UserPwd)? "admin":hangfireConfig.UserPwd
                            }
                        }
                    })
                }
            };
            app.UseHangfireDashboard(hangfireConfig.PathMatch, options);
            return app;
        }
    }

    /// <summary>
    /// hangfire的配置对象
    /// </summary>
    public class HangfireConfig
    {
        /// <summary>
        /// 是否启用
        /// </summary>
        public bool Enable { get; set; }

        /// <summary>
        /// 面板的路径
        /// </summary>
        public string? PathMatch { get; set; }

        /// <summary>
        /// 启用的库
        /// </summary>
        public int DataBase { get; set; }

        /// <summary>
        /// 成功的任务的过期时间（minutes）
        /// </summary>
        public int JobExpire { get; set; } = 30;

        /// <summary>
        /// 工作线程
        /// </summary>
        public int WorkerCount { get; set; }
        /// <summary>
        /// 轮询时间(s)
        /// </summary>
        public int SchedulePollingInterval { get; set; }
        /// <summary>
        /// 面板的登录名
        /// </summary>
        public string? UserLogin { get; set; }
        /// <summary>
        /// 面板的登录密码
        /// </summary>
        public string? UserPwd { get; set; }
    }

    /// <summary>
    /// 设置已完成job的过期时间(默认是24h),防止数据无限增长
    /// </summary>
    public class SucceededStateExpireHandler : IStateHandler
    {
        public TimeSpan JobExpirationTimeout;

        public SucceededStateExpireHandler(int jobExpirationTimeout)
        {
            JobExpirationTimeout = TimeSpan.FromMinutes(jobExpirationTimeout);
        }

        public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
            context.JobExpirationTimeout = JobExpirationTimeout;
        }

        public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {

        }

        /// <summary>
        /// 状态名
        /// </summary>
        public string StateName { get; } = SucceededState.StateName;
    }
    /// <summary>
    /// 被删除的数据的过期时间  也是1h  暂时不添加了   因为一般应该都是成功的
    /// </summary>
    public class DeletedStateExpireHandler : IStateHandler
    {
        public TimeSpan JobExpirationTimeout;

        public DeletedStateExpireHandler(int jobExpirationTimeout)
        {
            JobExpirationTimeout = TimeSpan.FromMinutes(jobExpirationTimeout);
        }

        public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
            context.JobExpirationTimeout = JobExpirationTimeout;
        }

        public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {

        }

        /// <summary>
        /// 状态名
        /// </summary>
        public string StateName { get; } = DeletedState.StateName;
    }

    /// <summary>
    /// 支持依赖注入
    /// </summary>
    public class HangfireActivator : JobActivator
    {
        //这里是在 HangfireActivator 的构造函数中把 AspNetCore 的 IOC 容器对象传入，并且重写 ActivateJob 方法，让 Hangfire 才激活任务的时候从 IOC 容器中获取对象
        private readonly IServiceProvider _serviceProvider;

        public HangfireActivator(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
        /// <summary>
        /// 默认使用的scop对象
        /// </summary>
        /// <param name="jobType"></param>
        /// <returns></returns>
        public override object? ActivateJob(Type jobType)
        {
            using var scope = _serviceProvider.CreateScope();
            return scope.ServiceProvider.GetService(jobType);
        }
    }

    public class LogEverythingAttribute : JobFilterAttribute,
        IClientFilter, IServerFilter, IElectStateFilter, IApplyStateFilter
    {
        
        public void OnCreating(CreatingContext context)
        {
            Log.Information("Creating a job based on method `{0}`...", context.Job.Method.Name);
        }

        public void OnCreated(CreatedContext context)
        {
            Log.Information(
                "Job that is based on method `{0}` has been created with id `{1}`",
                context.Job.Method.Name,
                context.BackgroundJob?.Id);
        }

        public void OnPerforming(PerformingContext context)
        {
            Log.Information("Starting to perform job `{0}`", context.BackgroundJob.Id);
        }

        public void OnPerformed(PerformedContext context)
        {
            Log.Information("Job `{0}` has been performed", context.BackgroundJob.Id);
        }

        public void OnStateElection(ElectStateContext context)
        {
            var failedState = context.CandidateState as FailedState;
            if (failedState != null)
            {
                Log.Information(
                    "Job `{0}` has been failed due to an exception `{1}`",
                    context.BackgroundJob.Id,
                    failedState.Exception);
            }
        }

        public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
            Log.Information(
                "Job `{0}` state was changed from `{1}` to `{2}`",
                context.BackgroundJob.Id,
                context.OldStateName,
                context.NewState.Name);
        }

        public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
            Log.Information(
                "Job `{0}` state `{1}` was unapplied.",
                context.BackgroundJob.Id,
                context.OldStateName);
        }
    }
    /// <summary>
    /// 记录任务执行时间的过滤器
    /// </summary>
    public class JobPerformTimeAttribute:JobFilterAttribute,IServerFilter
    {
        private DateTime _time;
        public void OnPerforming(PerformingContext filterContext)
        {
           _time = DateTime.Now;
        }

        public void OnPerformed(PerformedContext filterContext)
        {
            var time = (DateTime.Now-_time).Milliseconds;
            Console.WriteLine($"执行时间为：{time} 毫秒");
        }
    }
}
